/*
 * Example of calling Random L2-L1 solver for Synthetic problem
 *
 * This can be compiled with g++ compiler
 *
 * We will compare the Serial Random and Shrinking Strategy for Serial Random algorithm
 *
 * PS: If one introduce shrinking too early or with high probability, the result can be worse.
 *
 *   ulimit -s unlimited
 *   g++ -O3    -fopenmp mc_netflix.cpp
 *
 */
using namespace std;
#include <vector>
#include "../../helpers/c_libs_headers.h"
#include "../../utils/data_loader.h"
//#include "mc/mc_problem_generation.h"
//#include "helpers/matrix_conversions.h"

#include "../../solver/matrixCompletion/parallel/parallel_mc_opemmp.h"

//======================== Solvers
#include <omp.h>

template<typename T, typename I>
void runExample(float executionTime, float ex_mu, long ex_bulk, long ex_total,
		int ex_rank, int TT, T DELTA) {

	// ======================GENERATION COO RADNOM DATA=======================================
	problem_mc_data<I, T> ProblemData_instance;
	I m;
	I n;
	int status =
			load_matrix_in_coo_format(
//					"/home/taki/phd/otherProjects/matrixcompletionwithinequalities/matlab/netflix/smallnetflix_mm.validate",
					"/data/takac/nf/train_set.dat",
//			"/home/s1052689/mc/netflix/smallnetflix_mm.train",
//					"/home/taki/phd/otherProjects/matrixcompletionwithinequalities/matlab/netflix/smallnetflix_mm.train",
					ProblemData_instance.A_coo_values, //
					ProblemData_instance.A_coo_row_idx, //
					ProblemData_instance.A_coo_col_idx, //
					m, n);
	if (!status)
		return;

	cout << "Dimension of your problem is " << m << " x  " << n << " \n";
	cout << "Number of nonzero elements of matrix A: "
			<< ProblemData_instance.A_coo_values.size() << "\n";
	// ======================Configure Solver
	OptimizationStatistics statistics;
	OptimizationSettings settings; // Create setting for optimization procedure
	settings.total_execution_time = executionTime; //see solver/structures for more settings
	settings.bulkIterations = false;
	settings.showIntermediateObjectiveValue = false;
	settings.showLastObjectiveValue = true;
	settings.showInitialObjectiveValue = true;

	settings.verbose = true;

	settings.iters_bulkIterations_count = 1;

	ProblemData_instance.m = m;
	ProblemData_instance.n = n;
	I m_test;
	I n_test;
	problem_mc_data<I, T> test_data;
	status =
			load_matrix_in_coo_format(
//					"/home/taki/phd/otherProjects/matrixcompletionwithinequalities/matlab/netflix/smallnetflix_mm.validate",
					"/data/takac/nf/probe_set.dat",
//			"/home/s1052689/mc/netflix/smallnetflix_mm.validate",
					test_data.A_coo_values, //
					test_data.A_coo_row_idx, //
					test_data.A_coo_col_idx, //
					m_test, n_test);
	if (!status)
		return;

	I threads = TT;
	//	for (I threads = 1; threads <= 20; threads++) {
	settings.totalThreads = threads;

	//	for (int i = 200; i <= 20000; i = i * 10) {
	ProblemData_instance.mu = ex_mu;
	settings.iters_bulkIterations_count = ex_bulk;
	settings.iters_communicate_count = ex_total;
	ProblemData_instance.rank = ex_rank;
	settings.verbose = false;

	run_matrix_completion_openmp_solver(ProblemData_instance, test_data,
			settings, statistics, TT, DELTA);
	printf("Computation ends\n");
//	T prediction = 0;
//	long long total_test_points = 0;
//	long long total_correct_points = 0;
//	for (I j = 0; j < test_data.A_coo_row_idx.size(); j++) {
//		total_test_points++;
//		I row = test_data.A_coo_row_idx[j];
//		I col = test_data.A_coo_col_idx[j];
//		prediction = 0;
//		for (int i = 0; i < ProblemData_instance.rank; i++) {
//			prediction += ProblemData_instance.L_mat[row
//					* ProblemData_instance.rank + i]
//					* ProblemData_instance.R_mat[col
//							* ProblemData_instance.rank + i];
//		}
//		if (abs(test_data.A_coo_values[j] - prediction) < 0.5) {
//			total_correct_points++;
//		}
//
//		//		if (total_test_points < 10)
//		//			printf("D[%d,%d]=%f  p:%f\n", test_data.A_coo_col_idx[j],
//		//					test_data.A_coo_row_idx[j], test_data.A_coo_values[j],
//		//					prediction);
//	}
//	printf("T%d: Total points: %d, total successfull %d,  accuracy:%f\n",
//			settings.totalThreads, total_test_points,
//			test_data.A_coo_row_idx.size(), total_correct_points,
//			(float) total_correct_points / total_test_points);
//
//	T train_prediction = 0;
//	long long train_total_test_points = 0;
//	long long train_total_correct_points = 0;
//	for (I j = 0; j < ProblemData_instance.A_coo_row_idx.size(); j++) {
//		train_total_test_points++;
//		I row = ProblemData_instance.A_coo_row_idx[j];
//		I col = ProblemData_instance.A_coo_col_idx[j];
//		train_prediction = 0;
//		for (int i = 0; i < ProblemData_instance.rank; i++) {
//			train_prediction += ProblemData_instance.L_mat[row
//					* ProblemData_instance.rank + i]
//					* ProblemData_instance.R_mat[col
//							* ProblemData_instance.rank + i];
//		}
//		if (abs(ProblemData_instance.A_coo_values[j] - prediction) < 0.5) {
//			train_total_correct_points++;
//		}
//	}
//
//	printf("Tr:%d:Total points:%d, total successfull %d,  accuracy:%f\n",
//			settings.totalThreads, train_total_test_points,
//			train_total_correct_points, (float) train_total_correct_points
//					/ train_total_test_points);

	printf("%d:Average speed:%f it/ms \n", settings.totalThreads,
			statistics.average_speed_iters_per_ms);
	printf("%d:Total execution time:%f ms \n", settings.totalThreads,
			statistics.time_wallclock);
	printf("%d:Total Iterations %f M\n", settings.totalThreads,
			statistics.number_of_iters_in_millions);
	//	}
	//	}
	/*
	 // ======================Solve problem using Serial Random Coordinate Descent Method=========
	 cout << "Solve problem using Serial Random Coordinate Descent Method\n";
	 std::vector<float> h_x_result; // Vector where result will be stored


	 settings.use_shrinking = false;
	 settings.shrinking_probability = 0.9;
	 settings.shrinking_starting_iter = 3 * n;

	 settings.threshold = 1e-5;
	 settings.regulatization_path_length = 0;

	 settings.iters_bulkIterations_count = 10;
	 int numthreads = 2;
	 float averageSpeed[numthreads + 1];

	 ProblemData_instance.m = m;
	 ProblemData_instance.n = n;

	 ProblemData_instance.lambda = .01;
	 ProblemData_instance.sigma = 1;
	 ProblemData_instance.x.resize(n, 0);

	 for (int num_of_threads = 1; num_of_threads <= numthreads; num_of_threads++) {
	 settings.max_totalThreads = num_of_threads;

	 averageSpeed[num_of_threads]
	 = static_statsistics.average_speed_iters_per_ms;
	 }
	 for (int num_of_threads = 1; num_of_threads <= numthreads; num_of_threads++) {
	 printf("Thread %d, speed %f \n", num_of_threads,
	 averageSpeed[num_of_threads]);
	 }

	 */

}

int main(void) {

	srand(1);
	cout << "Example for L2L1 - start \n";
	int rank = 1;
	double delta = 1;
	int IT = 10;
	runExample<double, unsigned long>(200.10, 0.001, 2, IT, rank, 8, delta);
//	delta = 0.1;
//	runExample<double, unsigned long>(200.10, 0.001, 2, IT, rank, 8, delta);
	delta = 0.01;
	runExample<double, unsigned long>(200.10, 0.001, 2, IT, rank, 8, delta);

//	delta = 0.001;
//	runExample<double, unsigned long>(200.10, 0.001, 2, IT, rank, 8, delta);

	delta = 0.0001;
	runExample<double, unsigned long>(200.10, 0.001, 2, IT, rank, 8, delta);
	delta = 0;
	runExample<double, unsigned long>(200.10, 0.001, 2, IT, rank, 8, delta);

//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 1);
////	runExample<double, unsigned long long>(200.10, 0.001, 2, 10, rank, 2);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 4);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 8);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 16);
//
//	rank = 8;
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 1);
////	runExample<double, unsigned long long>(200.10, 0.001, 2, 10, rank, 2);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 4);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 8);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 16);
//
//	rank = 16;
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 1);
////	runExample<double, unsigned long long>(200.10, 0.001, 2, 10, rank, 2);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 4);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 8);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 16);
//
//	rank = 20;
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 1);
////	runExample<double, unsigned long long>(200.10, 0.001, 2, 10, rank, 2);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 4);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 8);
//	runExample<double, unsigned long long>(200.10, 0.001, 2, 50, rank, 16);

	cout << "Example for L2L1 - finish \n";
	return 0;
}
